package ycl.security.config;

import cn.hutool.crypto.SecureUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import ycl.security.handler.*;

import javax.annotation.Resource;

/**
 * 登录认证
 *
 * @author ycl
 * @date 2022-03-08 19:55:40
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@ComponentScan({"ycl"})
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Resource
	private AuthSuccessHandler authSuccessHandler;
	@Resource
	private AuthFailureHandler authFailureHandler;
	@Resource
	private AuthLogoutHandler authLogoutHandler;
	@Resource
	private AuthLogoutSuccessHandler authLogoutSuccessHandler;
	@Resource
	private AuthEntryPointHandler authEntryPointHandler;
	@Resource
	private AuthAccessDeniedHandler authAccessDeniedHandler;
	@Resource
	private AuthProvider authProvider;
	@Resource
	private AuthorizationFilter authorizationFilter;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) {
		auth.authenticationProvider(authProvider);
	}

	/**
	 * 配置 web 请求安全性
	 *
	 * @param web 前端请求
	 */
	@Override
	public void configure(WebSecurity web) {
		//放行特例资源
		String[] paths = {
				"/assets/**",
				"/css/**",
				"/swagger-ui.html",
				"/webjars/**",
				"/v2/**",
				"/swagger-resources/**",
				"/images/**",
				"/error",
		};
		web.ignoring().antMatchers(paths);
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new PasswordEncoder() {
			@Override
			public String encode(CharSequence rawPassword) {
				return SecureUtil.sha1((String) rawPassword);
			}

			@Override
			public boolean matches(CharSequence rawPassword, String encodedPassword) {
				String s = SecureUtil.sha1((String) rawPassword);
				return s.equals(encodedPassword);
			}
		};
	}


	/**
	 * 配置 http 请求安全性
	 *
	 * @param http http请求
	 * @throws Exception ecp
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		//配置不需要权限访问的路径
		String[] ignorePath = {
				//"/ycl-security/login/*",
		};
		http.csrf().disable().authorizeRequests()
				//配置不校验的路径
				.mvcMatchers(ignorePath).permitAll()
				.anyRequest().fullyAuthenticated()
				.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

				.and().formLogin()
				//配置登录入口
				.loginProcessingUrl("/login/login")
				//登录成功回调
				.successHandler(authSuccessHandler)
				//登录失败回调
				.failureHandler(authFailureHandler)

				.and().logout()
				//退出登录
				.logoutUrl("/logout/logout")
				.addLogoutHandler(authLogoutHandler)
				//退出成功
				.logoutSuccessHandler(authLogoutSuccessHandler)

				//认证失败处理
				.and().exceptionHandling()
				//未登录
				.authenticationEntryPoint(authEntryPointHandler)
				//未授权
				.accessDeniedHandler(authAccessDeniedHandler)
				.and().authenticationProvider(authProvider)
				.addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class)
		;
		http.headers().cacheControl();
	}


	@Bean
	@Override
	protected AuthenticationManager authenticationManager() throws Exception {
		return super.authenticationManager();
	}
}
